//
// Created by MIKE on 2018/12/18.
//

#include "bw_FcgiRequest.h"
#include "MsgList.h"
#include <errno.h>
#include "bw_file_tool.h"

namespace bwcgi
{

    MsgList::~MsgList()
    {

    }

    MsgList::MsgList(const char *path, int proId)
                : msgInit (false)
                , semInit (false)
                , key (-1)
                , msgId(-1)
                , semId(-1)
    {
        bool ret;
        ret = Upg_File::checkAndCreatePath(path);
        if(!ret)
        {
            cgi_debug("checkAndCreatePath error : %s", path);
            return;
        }

        key = ftok(path, proId);
        if(-1 == key)
        {
            cgi_debug("ftok error");
            return;
        }
        msgId = msgget(key, IPC_CREAT | 0664);
        if(-1 == msgId)
        {
            cgi_debug("msgget error");
            return;
        }

        msgInit = true;
    }

    bool MsgList::sendMsg(const void *msg, size_t msgLen)
    {
        bool ret;

        if(nullptr == msg || msgLen <= 0)
        {
            cgi_debug("SEND MSG PARAM ERROR");
            return false;
        }
        if(!msgInit)
        {
            cgi_debug("MSG LIST INIT ERROR");
            return false;
        }
        long type = getMsgType(msg);
        cgi_debug("TYPE = %ld", type);
        if(0 == type)
        {
            cgi_debug("MSG TYPE ERROR(type = %ld)", type);
            return false;
        }

        if(!semInit)
        {
            ret = initSemProducer();
            if(!ret)
            {
                cgi_debug("initSemProducer error");
                return false;
            }
        }
        if( -1 == msgsnd(msgId, msg, msgLen, 0))
        {
            cgi_debug("SEND MSG ERROR(errno = %d)", errno);
            return false;
        }
        if(!sem_v()){
            cgi_debug("sem_v ERROR");
            return false;
        }
#if 0
        /*  add for test    */
        int val = semctl(semId, 0, GETVAL, 0);
        if(-1 == val)
        {
            UPG_PRINT("semctl ERROR");
            return false;
        }
        UPG_PRINT("=============> VAL = %d", val);
#endif
        cgi_debug("success SEND");
        return true;
    }

    bool MsgList::getMsg(void *msg, size_t msgLen)
    {
        bool ret;

        if(nullptr == msg || msgLen <= 0)
        {
            cgi_debug("GET MSG PARAM ERROR");
            return false;
        }
        if(!msgInit)
        {
            cgi_debug("MSG LIST INIT ERROR");
            return false;
        }
        long type = getMsgType(msg);
        cgi_debug("TYPE = %ld", type);
        if(0 == type)
        {
            cgi_debug("MSG TYPE ERROR(type = %ld)", type);
            return false;
        }

        if(!semInit)
        {
            ret = initSemCustomer();
            if(!ret)
            {
                cgi_debug("initSemCustomer error");
                return false;
            }
        }
#if 0
        /*  add for test    */
        int val = semctl(semId, 0, GETVAL, 0);
        if(-1 == val)
        {
            UPG_PRINT("semctl ERROR");
            return false;
        }
        UPG_PRINT("=============> VAL = %d", val);
#endif
        if(!sem_p()){
            cgi_debug("sem_p ERROR");
            return false;
        }
        if( -1 == msgrcv(msgId, msg, msgLen, 0, IPC_NOWAIT))
        {
            cgi_debug("GET MSG ERROR(errno = %d)", errno);
            return false;
        }

        return true;
    }

    long MsgList::getMsgType(const void *msg)
    {
        if(msg == nullptr)
        {
            cgi_debug("PARAM ERROR");
            return 0;
        }
        const char * ptr = (const char*)msg;
        long type = 0x0;
        if(Upg_File::isBigEndian())
        {
            for (U32 i = 0; i < sizeof(type); i++) {
                type |= ptr[i] << (sizeof(type) - 1 - i) * 8;
            }
        }
        else
        {
            for (U32 i = 0; i < sizeof(type); i++) {
                type |= ptr[i] << i*8;
            }
        }
        return type;
    }

    void MsgList::deleteMsgList()
    {
        if(semId != -1)
        {
            if(-1 == semctl(semId, 0, IPC_RMID))
                cgi_debug("semctl error");
        }
        cgi_debug("init = %d, msgId = %d", msgInit, msgId);
        if(!msgInit || msgId == -1 ) {
            cgi_debug("init false || msgId == -1");
            return;
        }
        msgctl(msgId, IPC_RMID, nullptr);
    }

    bool MsgList::initSemProducer()
    {
        semId = semget(key, 1, IPC_CREAT | 0664);
        if(-1 == semId)
        {
            cgi_debug("semget error");
            return false;
        }

        union semun semum_args;
        semum_args.val = 0;
        if(-1 == semctl(semId, 0, SETVAL, semum_args))
        {
            cgi_debug("semctl setval error");
            return false;
        }

        semInit = true;
        return true;
    }

    bool MsgList::initSemCustomer()
    {
        semId = semget(key, 1, IPC_CREAT | 0664);
        if(-1 == semId)
        {
            cgi_debug("semget error");
            return false;
        }

        semInit = true;
        return true;
    }

    /*  生产者 */
    bool MsgList::sem_v() {
        sembuf sops;
        sops.sem_num = 0;
        sops.sem_flg = SEM_UNDO;
        sops.sem_op = 1;
        return semop(semId, &sops, 1) != -1;
    }

    /*  消费者 */
    bool MsgList::sem_p() {
        sembuf sops;
        sops.sem_num = 0;
        sops.sem_flg = SEM_UNDO;
        sops.sem_op = -1;

        timespec timeout;
        timeout.tv_sec = 2;
        timeout.tv_nsec = 0;
        return semtimedop(semId, &sops, 1, &timeout) != -1;
    }

    bool MsgList::msgIsInit() {
        return msgInit;
    }

    int MsgList::getSemVal() {
        if(!semInit)
            initSemCustomer();
        int value = semctl(semId, 0, GETVAL, 0);
        if(-1 == value)
            cgi_debug("semctl getval error");

        return value;
    }

    U32 MsgList::getMsgNum() {
        if(!msgInit)
            return MSG_NUM_ERROR;
        struct msqid_ds buf;
        memset(&buf, 0, sizeof(buf));
        buf.msg_qnum = MSG_NUM_ERROR;
        msgctl(msgId, IPC_STAT, &buf);

        return buf.msg_qnum;
    }

    bool MsgList::setSemVal(short val) {
        if(!semInit)
            initSemCustomer();

        union semun semum_args;
        semum_args.val = val;
        if(-1 == semctl(semId, 0, SETVAL, semum_args))
        {
            cgi_debug("semctl setval error");
            return false;
        }
        return true;
    }
}   /* namespace    */